﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GenericGA.reader.regex
{
    /// <summary>
    /// A regular expression node for iterating a child regular expression node for a finite number of times.
    /// </summary>
    /// <typeparam name="A">The type of the alphabet being matched.</typeparam>
    public class Iter<A> : Regex<A>
    {

        /// <summary>
        /// The regular expression to iterate.
        /// </summary>
        private readonly Regex<A> operand;

        /// <summary>
        /// The maximum and minimum number of iterations to perform.
        /// </summary>
        private readonly int maxIterations, minIterations;

        /// <summary>
        /// Create an iteration regular expression based on a maximum and minimum number of iterations.
        /// </summary>
        /// <param name="minIterations">The minimum number of iterations to perform.</param>
        /// <param name="maxIterations">The maximum number of iterations to perform.</param>
        /// <param name="operand">The regular expression to iterate.</param>
        public Iter(int minIterations, int maxIterations, Regex<A> operand)
        {
            if (minIterations < 0)
            {
                throw new ArgumentException("minIterations cannot be negative but was " + minIterations + ".");
            }
            if (maxIterations < 0)
            {
                throw new ArgumentException("maxIterations cannot be negative but was " + maxIterations + ".");
            }
            if (maxIterations < minIterations)
            {
                throw new ArgumentException("maxIterations cannot be smaller than minIterations but maxIterations was " + maxIterations + " and minIterations was " + minIterations + ".");
            }

            this.operand = operand;
            this.maxIterations = maxIterations;
            this.minIterations = minIterations;
        }

        /// <summary>
        /// Alias of Iter(int minIterations, int maxIterations, Regex<A> operand) but with operand at the start.
        /// </summary>
        /// <param name="minIterations">The minimum number of iterations to perform.</param>
        /// <param name="maxIterations">The maximum number of iterations to perform.</param>
        /// <param name="operand">The regular expression to iterate.</param>
        public Iter(Regex<A> operand, int minIterations, int maxIterations) : this(minIterations, maxIterations, operand) { }

        /// <summary>
        /// Constructor to iterate a regular expression for a deterministic fixed number of times.
        /// </summary>
        /// <param name="iterations">Number of times to iterate.</param>
        /// <param name="operand">The regular expression to iterate.</param>
        public Iter(int iterations, Regex<A> operand) : this(iterations, iterations, operand) { }

        /// <summary>
        /// Generate a list which is matched by the regular expression based on a reader object. In this case the iteration of a regular expression.
        /// </summary>
        /// <param name="reader">Reader object to determine which list to generate.</param>
        /// <returns>The iteration of regular expressions.</returns>
        public override List<A> Generate(Reader reader)
        {
            long iterations = reader.ReadInteger(minIterations, maxIterations);

            List<A> result = new List<A>();
            for (long i = 0; i < iterations; i++)
            {
                result.AddRange(operand.Generate(reader));
            }
            
            return result;
        }

        /// <summary>
        /// Enumerate every list which matches this regular expression.
        /// </summary>
        /// <returns>An iteration of the lists matched by the regular expression.</returns>
        public override IEnumerable<List<A>> Enumerate()
        {
            for (int iterations = minIterations; iterations <= maxIterations; iterations++)
            {
                foreach (List<A> result in Enumerate(iterations))
                {
                    yield return result;
                }
            }
        }

        /// <summary>
        /// Helper function to recursively enumerate each list which is matched by this iteration.
        /// </summary>
        /// <param name="iterationsLeft">The number of iterations left in the recursion process.</param>
        /// <returns>An enumeration of all the lists matched by the iteration of the operand for iterationsLeft iterations.</returns>
        private IEnumerable<List<A>> Enumerate(int iterationsLeft)
        {
            //Recursive case: If more iterations need to be done then return an enumeration of lists which match the operand concatenated together for iterationsLeft times
            if (iterationsLeft > 0)
            {
                //Enumerate all the lists matched by the operand and concatenate each one with each list matched by the concatenation of one less iteration of the operand
                foreach (List<A> head in operand.Enumerate())
                {
                    foreach (List<A> tail in Enumerate(iterationsLeft - 1))
                    {
                        List<A> result = new List<A>(head);
                        result.AddRange(tail);
                        yield return result;
                    }
                }
            }

            //Base case: Return empty list
            else
            {
                yield return new List<A>();
            }
        }

    }
}
